#!/usr/bin/python
# -*- coding: utf-8 -*-
#Programmed by Kaan Akşit

import sys,os,time,pygame,pygame.camera,numpy,cv,random
from pygame.locals import *
from numpy import *
from numpy.linalg import *
from math import *
from scipy.signal import *
from pylab import *
from scipy import *

class window:
    def __init__(self,width,height):
        localtime = time.asctime(time.localtime(time.time()))
        print "\x1b\x5b1;33;32m" + localtime + ', Kaan Akşit, 2011' + '\x1b[0m'
        print "\x1b\x5b1;31;40m" + 'Pygame version: '+ "\x1b[0m" + str(pygame.version.ver)
        pygame.init()
        self.height = height+40
        self.width  = width
        self.screen     = pygame.display.set_mode((self.width, self.height),pygame.HWSURFACE)
        print "\x1b\x5b1;31;40m" + 'Video info:'+' '+ "\x1b[0m",
        print "\x1b\x5b1;33;20m", pygame.display.Info()
        self.background       = pygame.Surface(self.screen.get_size())
        self.backrect         = self.background.get_rect()
        self.backrect.height -= 40
        self.background.set_colorkey((250, 250, 250))
        pygame.display.set_caption('bulutus')
    def text_blit(self,script,color,posx,posy,font_type,font_size):
        font            = pygame.font.Font(filepath(font_type),font_size)
        text            = font.render(unicode(script), 0, color)
        textpos         = text.get_rect()
        textpos.width  += 15
        textpos.height += 15
        textpos.centerx = posx
        textpos.centery = posy
        self.background.fill((0,0,0), textpos)
        self.background.blit(text,textpos)
        return textpos, script
    def drawline(self,ux,uy,d1,d2,color=(0,255,0)):
        start_pos = (ux,uy)
        end_pos   = (ux+d1,uy+d2)
        pygame.draw.line(self.background, color, start_pos, end_pos,1)
        return True
    def drawpolygon(self,ux,uy,d1,d2,color=(0,255,0)):
        step      = 2
        startpos1 = (ux+step,uy+step)
        startpos2 = (ux-step,uy-step)
        endpos    = (ux+d1,uy+d2)
        pygame.draw.polygon(self.background, color, (startpos1,endpos,startpos2),0)
        return True
    def refresh(self):
        self.screen.blit(self.background, (0, 0))
        pygame.display.flip()
        return True
    def blit(self,image,rect):
        self.background.blit(image,rect)
        return True

class camera:
    def __init__(self,width,height):
        pygame.camera.init()
        print pygame.camera.list_cameras()
        self.height = height
        self.width  = width
        self.area   = self.height*self.width
        self.camera = pygame.camera.Camera(pygame.camera.list_cameras()[0],(self.width,self.height),'RGB')
        self.camera.set_controls(True,True)
        self.camera.start()
        print "\x1b\x5b1;31;40m" + 'Camera control: '+ "\x1b[0m" + str(self.camera.get_controls())
        self.image          = self.camera.get_image()
        self.cache          = cv.CreateImage((self.width,self.height),cv.IPL_DEPTH_8U,3)
        return
    def captureframe(self):
        if self.camera.query_image():
            self.image          = self.camera.get_image()
            self.image          = pygame.transform.flip(self.image,True,False)
            self.cache          = cv.CreateImage((self.width,self.height),cv.IPL_DEPTH_8U,3)
            cv.SetData(self.cache,pygame.image.tostring(self.image,'RGB',False))
        return self.image,self.cache
        
class opencvcam:
    def __init__(self,width,height):
        self.camera = cv.CreateCameraCapture(0)
        cv.SetCaptureProperty(self.camera,cv.CV_CAP_PROP_FRAME_WIDTH, 640)
        cv.SetCaptureProperty(self.camera,cv.CV_CAP_PROP_FRAME_HEIGHT, 480) 
        return
    def captureframe(self):
        self.cache = cv.QueryFrame(self.camera)
        cv.CvtColor(self.cache, self.cache, cv.CV_BGR2RGB)
        self.image = pygame.image.frombuffer(self.cache.tostring(),cv.GetSize(self.cache),'RGB')
        self.rect  = self.image.get_rect()
        return self.image,self.cache

class enemy(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image     = pygame.image.load(filepath('images/ship3d.png'))
        self.image     = pygame.transform.smoothscale(self.image, (70, 70))
        self.rect      = self.image.get_rect()
        self.move      = 10
        self.x = (random.random()-0.5)*2*self.move+1
        self.y = (random.random()-0.5)*2*self.move+1
    def update(self):
        newpos = self.rect.move((self.x,self.y))
        while wdw.backrect.contains(newpos)==False:
            self.x = (random.random()-0.5)*2*self.move+1
            self.y = (random.random()-0.5)*2*self.move+1
            newpos = self.rect.move((self.x,self.y))
        self.rect = newpos
        return True
        
def filepath(filename):
    data_py  = os.path.abspath(os.path.dirname(__file__))
    if data_py == '/usr/bin':
        data_py = '/usr/share/bulutus'
    data_dir = os.path.normpath(os.path.join(data_py, 'data'))
    return os.path.join(data_dir, filename)

def rgb2yuv(image):
    [nx,ny,nc] = shape(image)
    for i in range(0,nx):
      for j in range(0,ny):
         a = dot(image[i,j,:].T,array([[0.299,0.587,0.114],[-0.14713,-0.28886,0.436],[0.615,-0.51499,-0.10001]]))
         image[i,j,:] = a.T
    return image

def gaussian(image):
    gauss = array([[0.00000067,0.00002292,0.00019117,0.00038771,0.00019117,0.00002292,0.00000067],
                   [0.00002292,0.00078633,0.00655965,0.01330373,0.00655965,0.00078633,0.00002292],
                   [0.00019117,0.00655965,0.05472157,0.11098164,0.05472157,0.00655965,0.00019117],
                   [0.00038771,0.01330373,0.11098164,0.22508352,0.11098164,0.01330373,0.00038771],
                   [0.00019117,0.00655965,0.05472157,0.11098164,0.05472157,0.00655965,0.00019117],
                   [0.00002292,0.00078633,0.00655965,0.01330373,0.00655965,0.00078633,0.00002292],
                   [0.00000067,0.00002292,0.00019117,0.00038771,0.00019117,0.00002292,0.00000067],
                   ])
    image = convolve2d(image,gauss)
    return image

def opencvlucaskanade(previousframe,nextframe,surfrect,speedx,speedy,windowsize):
    const         = 2 
    hlfw          = int(floor(windowsize/2))
    croprect      = pygame.Rect((surfrect.left-hlfw,surfrect.top-hlfw),(surfrect.width+windowsize,surfrect.height+windowsize))
    if wdw.backrect.contains(croprect)==False:
      croprect.center = wdw.backrect.center
    previmg       = cv.CreateImage(cv.GetSize(previousframe),cv.IPL_DEPTH_8U,1)
    nextimg       = cv.CreateImage(cv.GetSize(nextframe),cv.IPL_DEPTH_8U,1)
    cv.CvtColor(previousframe,previmg,cv.CV_RGB2GRAY)
    cv.CvtColor(nextframe,nextimg,cv.CV_RGB2GRAY)
    im1           = cv.GetSubRect(previmg,(croprect.left,croprect.top,croprect.width,croprect.height))
    im2           = cv.GetSubRect(nextimg,(croprect.left,croprect.top,croprect.width,croprect.height))
    velx          = cv.CreateImage((croprect.width,croprect.height),cv.IPL_DEPTH_32F,1)
    vely          = cv.CreateImage((croprect.width,croprect.height),cv.IPL_DEPTH_32F,1)
    for j in range(20):
        cv.Smooth(im1, im1, param1 = 7)
        cv.Smooth(im2, im2, param1 = 7)
    cv.CalcOpticalFlowLK(im1,im2,(windowsize,windowsize),velx,vely)  
    speedx = -(cv.Avg(velx)[0])*const
    speedy = -(cv.Avg(vely)[0])*const
    croprect.centerx += speedx
    croprect.centery += speedy
    return croprect.centerx,croprect.centery

def lucaskanade(previousframe,nextframe,surfrect,speedx,speedy,windowsize,test=0,const=1):
    hlfw          = int(floor(windowsize/2))
    croprect      = pygame.Rect((surfrect.left-hlfw,surfrect.top-hlfw),(surfrect.width+windowsize,surfrect.height+windowsize))
    if wdw.backrect.contains(croprect)==False:
      croprect.center = wdw.backrect.center
    previm        = previousframe.subsurface(croprect)
    nextim        = nextframe.subsurface(croprect)
    im1           = pygame.surfarray.array3d(previm)[:,:,0]
    im2           = pygame.surfarray.array3d(nextim)[:,:,0]
    im1           = gaussian(im1)
    im2           = gaussian(im2)
    [nx,ny]       = shape(im1)
    dsdx          = convolve2d(im1,array([[-0.25, 0.25],[-0.25, 0.25]])) + convolve2d(im2,array([[-0.25, 0.25],[-0.25, 0.25]]))
    dsdy          = convolve2d(im1,array([[-0.25,-0.25],[ 0.25, 0.25]])) + convolve2d(im2,array([[-0.25,-0.25],[ 0.25, 0.25]]))
    dsdt          = convolve2d(im1,array([[ 0.25, 0.25],[ 0.25, 0.25]])) + convolve2d(im2,array([[-0.25,-0.25],[-0.25,-0.25]]))
    dsdx          = dsdx[0:nx,0:ny]
    dsdy          = dsdy[0:nx,0:ny]
    dsdt          = dsdt[0:nx,0:ny]
    u             = zeros((shape(im1)))
    v             = zeros((shape(im2)))
    for i in range(hlfw,nx-hlfw):
      for j in range(hlfw,ny-hlfw): 
         curfx    =  dsdx[i-hlfw:i+hlfw,j-hlfw:j+hlfw].T.flatten()
         curfy    =  dsdy[i-hlfw:i+hlfw,j-hlfw:j+hlfw].T.flatten()
         curft    = -dsdt[i-hlfw:i+hlfw,j-hlfw:j+hlfw].T.flatten()
         A        = column_stack((curfx,curfy))
         U        = dot(pinv(dot(A.T,A)),dot(A.T,curft))
         u[i,j]   = U[1]
         v[i,j]   = U[0]
    speedx = mean(u)*const
    speedy = mean(v)*const
    if abs(speedx)>0 or abs(speedy)>0:
      print '%s\r' % ' '*50,
      print ' X-axis: ',speedx,' Y-axis: ',speedy,
    if test==1:
      return u+v
    return speedx,speedy

def test():
    windowsize         = 20
    clock              = pygame.time.Clock()
    sampleframe1       = pygame.image.load(filepath('images/sampleframe1.png'))
    sampleframe2       = pygame.image.load(filepath('images/sampleframe2.png'))
    sampleframe1       = pygame.transform.smoothscale(sampleframe1,(width,height))
    sampleframe2       = pygame.transform.smoothscale(sampleframe2,(width,height))
    specialrect        = pygame.Rect(windowsize/2,windowsize/2,width-windowsize,height-windowsize)
    array              = lucaskanade(sampleframe1,sampleframe2,specialrect,0,0,windowsize,1)
    img                = pygame.surfarray.make_surface(array)
    print array
    wdw.blit(img,img.get_rect())
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == QUIT or (e.type == KEYDOWN and e.key == K_ESCAPE):
                sys.exit()
                return
        clock.tick()
        wdw.text_blit(('FPS: '+ str(int(clock.get_fps()))),[255,255,255],200,height+25,'font/slkscrb.ttf',20)
        wdw.refresh()
    return True

def main():
    global wdw,width,height,cloud3d
    fps    = 40
    width  = 640;height=480
    speedx = 0;speedy=0
    score  = 0;lives=3
    wdw                   = window(width,height)
    cam                   = camera(width,height)
    clock                 = pygame.time.Clock()
    redcyan3d             = pygame.image.load(filepath('images/redcyan.png'))
    redcyan3d             = pygame.transform.smoothscale(redcyan3d, (60, 25))
    redcyan3drect         = redcyan3d.get_rect()
    redcyan3drect.left    = width-70
    redcyan3drect.top     = height+10
    cloud                 = pygame.image.load(filepath('images/cloud.png'))
    cloud3d               = pygame.image.load(filepath('images/cloud3d.png'))
    cloud3d               = pygame.transform.smoothscale(cloud3d, (80, 70))
    cloud3drect           = cloud3d.get_rect()
    cloud3drect.center    = wdw.backrect.center
    ship                  = enemy()
    pygame.display.set_icon(cloud)
    [previmg,im1] = cam.captureframe()    
    [nextimg,im2] = cam.captureframe()
    #test()
    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == QUIT or (e.type == KEYDOWN and e.key == K_ESCAPE):
                sys.exit()
                return
        [previmg,im1] = cam.captureframe()    
        [speedx,speedy] = opencvlucaskanade(im1,im2,cloud3drect,speedx,speedy,15)
        cloud3drect.centerx = speedx
        cloud3drect.centery = speedy
        [nextimg,im2]     = cam.captureframe()
        clock.tick(fps)
        wdw.background.fill((0,0,0))
        wdw.blit(nextimg,wdw.backrect)
        wdw.blit(cloud3d,cloud3drect)
        wdw.blit(redcyan3d,redcyan3drect)
        wdw.text_blit(('Score: '+ str(score)),[255,255,255],70,height+25,'font/slkscrb.ttf',20)
        wdw.text_blit(('Lives: '+ str(lives)),[255,255,255],220,height+25,'font/slkscrb.ttf',20)
        wdw.text_blit(('FPS: '+ str(int(clock.get_fps()))),[255,255,255],360,height+25,'font/slkscrb.ttf',20)
        ship.update()
        pygame.sprite.RenderUpdates(ship).draw(wdw.background)
        wdw.refresh()
    return

if __name__ == "__main__":
    sys.exit(main())